/*     */ package com.zimbra.cs.datasource.imap;
/*     */ 
/*     */ import com.google.common.annotations.VisibleForTesting;
/*     */ import com.google.common.base.Joiner;
/*     */ import com.zimbra.common.service.ServiceException;
/*     */ import com.zimbra.common.util.Log;
/*     */ import com.zimbra.common.util.ZimbraLog;
/*     */ import com.zimbra.common.zmime.ZMimeMessage;
/*     */ import com.zimbra.common.zmime.ZSharedFileInputStream;
/*     */ import com.zimbra.cs.datasource.SyncUtil;
/*     */ import com.zimbra.cs.mailbox.Message;
/*     */ import com.zimbra.cs.mailclient.MailConfig;
/*     */ import com.zimbra.cs.mailclient.imap.AppendResult;
/*     */ import com.zimbra.cs.mailclient.imap.CAtom;
/*     */ import com.zimbra.cs.mailclient.imap.Envelope;
/*     */ import com.zimbra.cs.mailclient.imap.Flags;
/*     */ import com.zimbra.cs.mailclient.imap.ImapConnection;
/*     */ import com.zimbra.cs.mailclient.imap.ImapRequest;
/*     */ import com.zimbra.cs.mailclient.imap.ImapResponse;
/*     */ import com.zimbra.cs.mailclient.imap.Literal;
/*     */ import com.zimbra.cs.mailclient.imap.MailboxInfo;
/*     */ import com.zimbra.cs.mailclient.imap.MailboxName;
/*     */ import com.zimbra.cs.mailclient.imap.MessageData;
/*     */ import com.zimbra.cs.mailclient.imap.ResponseText;
/*     */ import com.zimbra.cs.store.MailboxBlob;
/*     */ import com.zimbra.cs.store.StoreManager;
/*     */ import java.io.File;
/*     */ import java.io.IOException;
/*     */ import java.io.InputStream;
/*     */ import java.util.ArrayList;
/*     */ import java.util.Date;
/*     */ import java.util.Iterator;
/*     */ import java.util.List;
/*     */ import java.util.Map;
/*     */ import javax.mail.MessagingException;
/*     */ import javax.mail.internet.MailDateFormat;
/*     */ import javax.mail.internet.MimeMessage;
/*     */ import javax.mail.util.SharedByteArrayInputStream;
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ public class ImapAppender
/*     */ {
/*     */   private final ImapConnection connection;
/*     */   private final String mailbox;
/*     */   private boolean hasAppendUid;
/*     */   private MailDateFormat mdf;
/*     */   
/*     */   public ImapAppender(ImapConnection connection, String mailbox)
/*     */   {
/*  62 */     this.connection = connection;
/*  63 */     this.mailbox = mailbox;
/*  64 */     this.hasAppendUid = connection.hasUidPlus();
/*     */   }
/*     */   
/*     */   public ImapAppender setHasAppendUid(boolean hasAppendUid) {
/*  68 */     this.hasAppendUid = hasAppendUid;
/*  69 */     return this;
/*     */   }
/*     */   
/*     */   public long appendMessage(Message msg) throws IOException, ServiceException {
/*  73 */     return append(new MessageInfo(msg));
/*     */   }
/*     */   
/*     */   @VisibleForTesting
/*     */   public long appendMessage(byte[] b, Flags flags) throws IOException, ServiceException {
/*  78 */     return append(new MessageInfo(getData(b), flags));
/*     */   }
/*     */   
/*     */   private long append(MessageInfo mi) throws IOException, ServiceException {
/*  82 */     InputStream is = mi.data.getInputStream();
/*  83 */     Literal lit = new Literal(is, mi.data.getSize());
/*     */     try {
/*  85 */       return this.hasAppendUid ? append(mi, lit) : appendSlow(mi, lit);
/*     */     } catch (MessagingException e) {
/*  87 */       throw ServiceException.FAILURE("Parsing error", e);
/*     */     } finally {
/*  89 */       is.close();
/*     */     }
/*     */   }
/*     */   
/*     */   private static Data getData(File file) {
/*  94 */     new Data()
/*     */     {
/*     */       public InputStream getInputStream() throws IOException {
/*  97 */         return new ZSharedFileInputStream(this.val$file);
/*     */       }
/*     */       
/*     */       public int getSize()
/*     */       {
/* 102 */         return (int)this.val$file.length();
/*     */       }
/*     */     };
/*     */   }
/*     */   
/*     */   private long append(MessageInfo mi, Literal data) throws IOException {
/* 108 */     ImapRequest req = this.connection.newRequest(CAtom.APPEND, new Object[] { new MailboxName(this.mailbox) });
/* 109 */     if (mi.flags != null) {
/* 110 */       req.addParam(mi.flags);
/*     */     }
/* 112 */     if (mi.date != null) {
/* 113 */       req.addParam(mi.date);
/*     */     }
/* 115 */     req.addParam(data);
/* 116 */     ResponseText rt = req.sendCheckStatus().getResponseText();
/* 117 */     if ((rt != null) && (rt.getCCode() == CAtom.APPENDUID)) {
/* 118 */       AppendResult ar = (AppendResult)rt.getData();
/* 119 */       if (ar.getUid() > 0L) {
/* 120 */         return ar.getUid();
/*     */       }
/*     */     }
/* 123 */     throw req.failed("APPENDUID supported but UID missing from result");
/*     */   }
/*     */   
/*     */   private long appendSlow(MessageInfo mi, Literal lit) throws IOException, MessagingException
/*     */   {
/* 128 */     MailboxInfo mb = this.connection.getMailboxInfo();
/* 129 */     if (this.mdf == null) {
/* 130 */       this.mdf = new MailDateFormat();
/*     */     }
/* 132 */     if ((mb == null) || (!this.mailbox.equals(mb.getName()))) {
/* 133 */       this.connection.select(this.mailbox);
/*     */     }
/* 135 */     long startUid = getUidNext();
/* 136 */     this.connection.append(this.mailbox, mi.flags, mi.date, lit);
/* 137 */     if ((mi.mm.getSentDate() == null) || (mi.mm.getMessageID() == null))
/*     */     {
/* 139 */       return -1L;
/*     */     }
/*     */     try
/*     */     {
/* 143 */       this.connection.select(this.mailbox);
/* 144 */       long endUid = getUidNext() - 1L;
/* 145 */       if (startUid <= endUid) {
/* 146 */         List<Long> found = findUids(startUid + ":" + endUid, mi);
/* 147 */         if (found.size() == 1) {
/* 148 */           return ((Long)found.get(0)).longValue();
/*     */         }
/*     */       }
/*     */       
/*     */ 
/*     */ 
/*     */ 
/*     */       List<Long> uids;
/*     */       
/*     */ 
/*     */ 
/*     */       try
/*     */       {
/* 161 */         this.connection.setReadTimeout(120);
/* 162 */         uids = this.connection.uidSearch(getSearchParams(mi));
/*     */       } finally {
/* 164 */         this.connection.setReadTimeout(this.connection.getConfig().getReadTimeout());
/*     */       }
/* 166 */       Object it = uids.iterator();
/* 167 */       while (((Iterator)it).hasNext()) {
/* 168 */         List<Long> found = findUids(nextSeq((Iterator)it, 5), mi);
/* 169 */         if (found.size() > 0) {
/* 170 */           if (found.size() > 1) {
/* 171 */             ZimbraLog.imap_client.warn("found more than one (%d)matching UID during appendSlow. Probably a leftover dupe from earlier bugs?", new Object[] { Integer.valueOf(found.size()) });
/*     */             
/* 173 */             if (ZimbraLog.imap_client.isDebugEnabled()) {
/* 174 */               ZimbraLog.imap_client.debug("potential duplicate ids = %s", new Object[] { Joiner.on(',').join(found) });
/*     */             }
/*     */           }
/* 177 */           return ((Long)found.get(0)).longValue();
/*     */         }
/*     */       }
/*     */     }
/*     */     catch (Exception e)
/*     */     {
/* 183 */       ZimbraLog.imap_client.warn("Dedupe search in appendSlow failed.", e);
/*     */     }
/*     */     
/*     */ 
/* 187 */     ZimbraLog.imap_client.warn("append slow failed to find appended message id");
/*     */     
/* 189 */     return -1L;
/*     */   }
/*     */   
/*     */   private Object[] getSearchParams(MessageInfo mi) throws MessagingException {
/* 193 */     List<Object> params = new ArrayList();
/* 194 */     params.add("SENTON");
/* 195 */     Date date = mi.mm.getSentDate();
/* 196 */     params.add(String.format("%td-%tb-%tY", new Object[] { date, date, date }));
/* 197 */     String mId = mi.mm.getMessageID();
/* 198 */     params.add("HEADER");
/* 199 */     params.add("message-id");
/* 200 */     params.add(mId);
/* 201 */     return params.toArray();
/*     */   }
/*     */   
/*     */   private String nextSeq(Iterator<Long> it, int count) {
/* 205 */     assert (it.hasNext());
/* 206 */     StringBuilder sb = new StringBuilder();
/* 207 */     sb.append(it.next());
/* 208 */     for (;;) { count--; if ((count <= 0) || (!it.hasNext())) break;
/* 209 */       sb.append(',').append(it.next());
/*     */     }
/* 211 */     return sb.toString();
/*     */   }
/*     */   
/*     */   private List<Long> findUids(String seq, MessageInfo mi) throws IOException, MessagingException {
/* 215 */     List<Long> uids = new ArrayList(1);
/* 216 */     Map<Long, MessageData> mds = this.connection.uidFetch(seq, "(RFC822.SIZE ENVELOPE)");
/* 217 */     for (MessageData md : mds.values()) {
/* 218 */       if (matches(mi, md)) {
/* 219 */         uids.add(Long.valueOf(md.getUid()));
/*     */       }
/*     */     }
/* 222 */     return uids;
/*     */   }
/*     */   
/*     */   private boolean matches(MessageInfo mi, MessageData md)
/*     */     throws MessagingException
/*     */   {
/* 228 */     Envelope env = md.getEnvelope();
/* 229 */     if (env != null) {
/* 230 */       String subj = mi.mm.getSubject();
/* 231 */       return (mi.mm.getMessageID().equals(env.getMessageId())) && ((subj == null) || (subj.equals(env.getSubject())));
/*     */     }
/* 233 */     return false;
/*     */   }
/*     */   
/*     */   private long getUidNext() throws IOException {
/* 237 */     return this.connection.status(this.mailbox, new Object[] { "UIDNEXT" }).getUidNext();
/*     */   }
/*     */   
/*     */ 
/* 241 */   private static Data getData(byte[] b) { new Data()
/*     */     {
/*     */       public InputStream getInputStream() throws IOException {
/* 244 */         return new SharedByteArrayInputStream(this.val$b);
/*     */       }
/*     */       
/*     */ 
/*     */ 
/* 249 */       public int getSize() { return this.val$b.length; }
/*     */     }; }
/*     */   
/*     */   private static abstract interface Data { public abstract InputStream getInputStream() throws IOException;
/*     */     
/*     */     public abstract int getSize() throws IOException; }
/*     */   
/*     */   private static class MessageInfo { ImapAppender.Data data;
/*     */     MimeMessage mm;
/*     */     Flags flags;
/*     */     Date date;
/*     */     
/* 261 */     MessageInfo(Message msg) throws ServiceException { final MailboxBlob mblob = msg.getBlob();
/* 262 */       this.data = new ImapAppender.Data()
/*     */       {
/*     */         public InputStream getInputStream() throws IOException {
/* 265 */           return StoreManager.getInstance().getContent(mblob);
/*     */         }
/*     */         
/*     */         public int getSize() throws IOException
/*     */         {
/* 270 */           return (int)mblob.getSize();
/*     */         }
/* 272 */       };
/* 273 */       this.mm = msg.getMimeMessage(false);
/* 274 */       this.flags = SyncUtil.zimbraToImapFlags(msg.getFlagBitmask());
/* 275 */       this.date = SyncUtil.getInternalDate(msg, this.mm);
/*     */     }
/*     */     
/*     */     MessageInfo(ImapAppender.Data data, Flags flags) throws ServiceException, IOException {
/* 279 */       this.data = data;
/* 280 */       InputStream is = data.getInputStream();
/*     */       try {
/* 282 */         this.mm = new ZMimeMessage(null, is);
/* 283 */         this.flags = flags;
/* 284 */         this.date = this.mm.getReceivedDate();
/* 285 */         if (this.date == null) {
/* 286 */           this.date = new Date(System.currentTimeMillis());
/*     */         }
/*     */       } catch (MessagingException e) {
/* 289 */         throw ServiceException.FAILURE("Unable to parse message", e);
/*     */       } finally {
/* 291 */         is.close();
/*     */       }
/*     */     }
/*     */   }
/*     */ }


/* Location:              /home/mint/zimbrastore.jar!/com/zimbra/cs/datasource/imap/ImapAppender.class
 * Java compiler version: 7 (51.0)
 * JD-Core Version:       0.7.1
 */